#include "graph.h"
#include "graphd.h"
#include "iostream"
#include "algorithm_graphd.h"
#include "algorithm_graph.h"

class graph_test
{
public:
    graph_test();
    ~graph_test();
    void run();
public:
    PUndirectedGraph G;

};

graph_test::graph_test()
{
    std::vector<std::string> tagKeys = { "time", "processId", "src_ip","dst_ip"};
    G = UndirectedGraph::New(tagKeys);
    G->AddLogNode("file1", 0);
    G->AddTagNode("time", "16:53");
    G->AddTagNode("processId", "8976");
    G->AddTagNode("src_ip", "22.22.22.3");
    G->AddTagNode("dst_ip", "33.33.33.2");

    G->AddEdge("file1", 0, "time", "16:53");
    G->AddEdge("file1", 0, "processId", "8976");
    G->AddEdge("file1", 0, "src_ip", "22.22.22.3");
    G->AddEdge("file1", 0, "dst_ip", "33.33.33.2");

    G->AddLogNode("file2", 1);
    G->AddTagNode("time", "16:54");
    G->AddTagNode("processId", "8976");
    G->AddTagNode("src_ip", "22.22.22.4");
    G->AddTagNode("dst_ip", "33.33.33.5");

    G->AddEdge("file2", 1, "time", "16:54");
    G->AddEdge("file2", 1, "processId", "8976");
    G->AddEdge("file2", 1, "src_ip", "22.22.22.4");
    G->AddEdge("file2", 1, "dst_ip", "33.33.33.5");

    G->AddLogNode("file3", 2);
    G->AddTagNode("time", "16:55");
    G->AddTagNode("processId", "8977");
    G->AddTagNode("src_ip", "22.22.22.4");
    G->AddTagNode("dst_ip", "33.33.33.8");

    G->AddEdge("file3", 2, "time", "16:55");
    G->AddEdge("file3", 2, "processId", "8977");
    G->AddEdge("file3", 2, "src_ip", "22.22.22.4");
    G->AddEdge("file3", 2, "dst_ip", "33.33.33.8");

    G->AddLogNode("file4", 3);
    G->AddTagNode("time", "16:54");
    G->AddTagNode("processId", "8976");
    G->AddTagNode("src_ip", "22.22.22.5");
    G->AddTagNode("dst_ip", "33.33.33.2");

    G->AddEdge("file4", 3, "time", "16:54");
    G->AddEdge("file4", 3, "processId", "8976");
    G->AddEdge("file4", 3, "src_ip", "22.22.22.5");
    G->AddEdge("file4", 3, "dst_ip", "33.33.33.2");
}

graph_test::~graph_test()
{
}

void graph_test::run()
{
    std::cout << G->BriefInfo() << "\n";
    //AlgorithmUndirectedGraph *ag = new AlgorithmUndirectedGraph(G);
    std::vector<int>neighborid = AlgorithmUndirectedGraph::Find_neighbor(G,0);
    for (int i = 0;i < neighborid.size();i++) { std::cout << AlgorithmUndirectedGraph::Evaluate(G,neighborid[i])<<" "; }
    std::cout << std::endl;

    std::vector<int>topk1 = AlgorithmUndirectedGraph::Find_Topk_neighbor(G,0, 1);
    for (int i = 0;i < topk1.size();i++) { std::cout << topk1[i] << " "; }
    std::cout << std::endl;

    std::vector<int>topk2 = AlgorithmUndirectedGraph::Find_Topk_neighbor(G,0, 2);
    for (int i = 0;i < topk2.size();i++) { std::cout << topk2[i] << " "; }
    std::cout << std::endl;

    std::vector<int>topk3 = AlgorithmUndirectedGraph::Find_Topk_neighbor(G,0, 3);
    for (int i = 0;i < topk3.size();i++) { std::cout << topk3[i] << " "; }
    std::cout << std::endl;

    std::vector<int>recommend = AlgorithmUndirectedGraph::Find_recommend_path(G,0);
    for (int i = 0;i < recommend.size();i++) { std::cout << recommend[i] << " "; }
    std::cout << std::endl;
}

void saveTest() {
    std::vector<std::string> tagKeys = {"time", "processId"};
    UndirectedGraph G(tagKeys);

    G.AddLogNode("file1", 0);
    G.AddTagNode("time", "111");
    G.AddTagNode("processId", "222");

    G.AddEdge("file1", 0, "time", "111");
    G.AddEdge("file1", 0, "processId", "222");
    G.AddEdge("file1", 0, "processId", "222");

    G.Save("../out_files/o.dat");
}

void loadTest() {
    UndirectedGraph G("../out_files/o.dat");
    std::cout << "Hello" << std::endl;
}

void saveTest1() {
    std::vector<std::string> tagKeys = {"time", "processId"};
    PUndirectedGraph G = UndirectedGraph::New(tagKeys);
    G->AddLogNode("file1", 0);
    G->AddTagNode("time", "111");
    G->AddTagNode("processId", "222");

    G->AddEdge("file1", 0, "time", "111");
    G->AddEdge("file1", 0, "processId", "222");
    G->AddEdge("file1", 0, "processId", "222");

    G->Save("../out_files/o1.dat");
    std::cout << G->BriefInfo() << "\n";
}

void loadTest1() {
    PUndirectedGraph G = UndirectedGraph::Load(
            "../out_files/o1.dat");
    std::cout << G->BriefInfo() << "\n";
}

void copyTest() {
    std::vector<std::string> tagKeys = {"time", "processId"};
    PUndirectedGraph G = UndirectedGraph::New(tagKeys);
    G->AddLogNode("file1", 0);
    G->AddTagNode("time", "111");
    G->AddTagNode("processId", "222");

    G->AddEdge("file1", 0, "time", "111");
    G->AddEdge("file1", 0, "processId", "222");

    PUndirectedGraph G1 = G->MakeCopy();
    G1->AddLogNode("file2", 0);
    G->DelNode(0);
    std::cout << G->BriefInfo() << std::endl;
    std::cout << G1->BriefInfo() << std::endl;
}

void dSaveTest() {
    PDirectedGraph G = DirectedGraph::New();
    int idFile0 = G->AddNode("File0", 1, 2);
    int idFile1 = G->AddNode("File1", 3, 4);
    int idFile2 = G->AddNode("File2", 5, 6);
    int idFileX = G->AddNode("FileX", 7, 8);
    int idPrA = G->AddNode("ProcessA", 9, 10);
    int idPrB = G->AddNode("ProcessB", 10, 11);
    int idPrC = G->AddNode("ProcessC", 12, 13);
    int idPrD = G->AddNode("ProcessD", 13, 14);

    G->AddEdge(idFile0, idPrA, "read", 3, "aa///1");
    G->AddEdge(idFile1, idPrC, "read", 5, "aa///2");
    G->AddEdge(idFile2, idPrC, "read", 7, "aa///3");

    G->AddEdge(idPrB, idFile1, "write", 1, "aa///7");
    G->AddEdge(idPrB, idFile2, "write", 2, "aa///8");
    G->AddEdge(idPrC, idFileX, "write", 6, "aa///9");

    G->AddEdge(idPrA, idPrB, "fork", 0, "aa///4");
    G->AddEdge(idPrA, idPrD, "fork", 8, "aa///5");
    G->AddEdge(idPrA, idPrC, "fork", 4, "aa///6");
    G->Save("../out_files/o2.dat");
    std::cout << G->BriefInfo() << std::endl;

}

void dLoadTest() {
    PDirectedGraph G1 = DirectedGraph::Load(
            "../out_files/o2.dat");
    std::cout << G1->BriefInfo() << std::endl;
}

void dMakeCopy() {
    PDirectedGraph G1 = DirectedGraph::Load(
            "../out_files/o2.dat");
    PDirectedGraph G2 = G1->MakeCopy();
    G2->AddNode("Dumb", 1, 3);
    std::cout << G1->BriefInfo() << std::endl;
    std::cout << G2->BriefInfo() << std::endl;

    int idPrA = G1->GetNodeId("ProcessA")[0];
    G1->DelNode(idPrA);
    std::cout << G1->BriefInfo() << std::endl;

}

void testTSVAndSave() {
    std::string path = "../in_files/t1-t2-t3-0.tsv";
    PDirectedGraph PG = DirectedGraph::New();
    AlgorithmDirectedGraph::LoadOneTsv(PG, path);
    std::cout << PG->BriefInfo() << std::endl;
    PG->Save("../out_files/o_test_tsv.dat");
}

PDirectedGraph getBackTrackingGraph() {
    PDirectedGraph G = DirectedGraph::New();
    int idFile0 = G->AddNode("File0", 1, 2);
    int idFile1 = G->AddNode("File1", 3, 4);
    int idFile2 = G->AddNode("File2", 5, 6);
    int idFileX = G->AddNode("FileX", 7, 8);
    int idPrA = G->AddNode("ProcessA", 9, 10);
    int idPrB = G->AddNode("ProcessB", 10, 11);
    int idPrC = G->AddNode("ProcessC", 12, 13);
    int idPrD = G->AddNode("ProcessD", 13, 14);

    G->AddEdge(idFile0, idPrA, "read", 3, "aa///1");
    G->AddEdge(idFile1, idPrC, "read", 5, "aa///2");
    G->AddEdge(idFile2, idPrC, "read", 7, "aa///3");

    G->AddEdge(idPrB, idFile1, "write", 1, "aa///7");
    G->AddEdge(idPrB, idFile2, "write", 2, "aa///8");
    G->AddEdge(idPrC, idFileX, "write", 6, "aa///9");

    G->AddEdge(idPrA, idPrB, "fork", 0, "aa///4");
    G->AddEdge(idPrA, idPrD, "fork", 8, "aa///5");
    G->AddEdge(idPrA, idPrC, "fork", 4, "aa///6");
    return G;
}

void testDTracking() {
    PDirectedGraph G = DirectedGraph::New();
    int idFile0 = G->AddNode("File0", 1, 2);
    int idFile1 = G->AddNode("File1", 3, 4);
    int idFile2 = G->AddNode("File2", 5, 6);
    int idFileX = G->AddNode("FileX", 7, 8);
    int idPrA = G->AddNode("ProcessA", 9, 10);
    int idPrB = G->AddNode("ProcessB", 10, 11);
    int idPrC = G->AddNode("ProcessC", 12, 13);
    int idPrD = G->AddNode("ProcessD", 13, 14);

    G->AddEdge(idFile0, idPrA, "read", 3, "aa///1");
    G->AddEdge(idFile1, idPrC, "read", 5, "aa///2");
    G->AddEdge(idFile2, idPrC, "read", 7, "aa///3");

    G->AddEdge(idPrB, idFile1, "write", 1, "aa///7");
    G->AddEdge(idPrB, idFile2, "write", 2, "aa///8");
    G->AddEdge(idPrC, idFileX, "write", 6, "aa///9");

    G->AddEdge(idPrA, idPrB, "fork", 0, "aa///4");
    G->AddEdge(idPrA, idPrD, "fork", 8, "aa///5");
    G->AddEdge(idPrA, idPrC, "fork", 4, "aa///6");
    std::cout << G->BriefInfo() << std::endl;
    std::cout << "--- --- ---" << std::endl;

    PDirectedGraph G1 = AlgorithmDirectedGraph::TrackGraph(G, {idFileX}, 3, 1, 10);
    std::cout << G1->BriefInfo() << std::endl;
}

void testIterator() {
    PDirectedGraph G = DirectedGraph::New();
    AlgorithmDirectedGraph::LoadOneTsv(G, "../test_files/thusword3-opencart-apache.tsv");
    for (auto iter = G->BeginNodeI(); iter != G->EndNodeI(); iter++) {
        std::cout << std::to_string(iter.GetId()) << ": " << iter.GetName() << std::endl;
    }
    std::cout << "------ ------" << std::endl;
    for (auto iter = G->BeginEdgeI(); iter != G->EndEdgeI(); iter++) {
        std::cout << std::to_string(iter.GetId()) << ": " <<
                  G->GetNodeNIdI(iter.GetSrcNId()).GetName() << "->" << G->GetNodeNIdI(iter.GetDstNId()).GetName()
                  << std::endl;
    }
}

void testTrackingWithApache() {
    PDirectedGraph G = DirectedGraph::New();
    AlgorithmDirectedGraph::LoadOneTsv(G, "../test_files/thusword3-opencart-apache.tsv");
    PDirectedGraph G1 = AlgorithmDirectedGraph::TrackGraph(G, {148}, 2, 1, 10);

    for (auto iter = G1->BeginNodeI(); iter != G1->EndNodeI(); iter++) {
        std::cout << std::to_string(iter.GetId()) << ": " << iter.GetName() << std::endl;
    }
    std::cout << "------ ------" << std::endl;
    for (auto iter = G1->BeginEdgeI(); iter != G1->EndEdgeI(); iter++) {
        std::cout << std::to_string(iter.GetId()) << ": " <<
                  G1->GetNodeNIdI(iter.GetSrcNId()).GetName() << "->" << G1->GetNodeNIdI(iter.GetDstNId()).GetName()
                  << std::endl;
    }
}

void testTrackingWithBash() {
    PDirectedGraph G = DirectedGraph::Load(
            "/Users/jinyuzhu/Documents/dev/provdb/provdb-client/provdb-client-data/thusword03/provdb/auditd/thusword03-provdb-auditd.pkl");
    PDirectedGraph G1 = AlgorithmDirectedGraph::TrackGraph(G, {208}, 0, 1, 10);

    for (auto iter = G1->BeginNodeI(); iter != G1->EndNodeI(); iter++) {
        std::cout << std::to_string(iter.GetId()) << ": " << iter.GetName() << std::endl;
    }
    std::cout << "------ ------" << std::endl;
    for (auto iter = G1->BeginEdgeI(); iter != G1->EndEdgeI(); iter++) {
        std::cout << std::to_string(iter.GetId()) << ": " <<
                  G1->GetNodeNIdI(iter.GetSrcNId()).GetName() << "->" << G1->GetNodeNIdI(iter.GetDstNId()).GetName()
                  << std::endl;
    }
}

void testReadCollected() {
    PUndirectedGraph G = UndirectedGraph::Load("/Users/jinyuzhu/Documents/dev/provdb-v2.1/provdb-tag-graph-script/graph.dat");
    for (auto iter = G->BeginNodeI(); iter != G->EndNodeI(); iter++) {
        std::cout << std::to_string(iter.GetId()) << ": " << iter.GetName() << std::endl;
    }
    std::cout << G->BriefInfo() << std::endl;
}

int main() {
//    copyTest();

 //   saveTest1();
//    loadTest1();

//    dSaveTest();
//    dLoadTest();

//    dSaveTest();
//    dMakeCopy();

//    testTSVAndSave();

//    testDTracking();

//    testIterator();

//    testTrackingWithApache();

//    testTrackingWithBash();

    testReadCollected();


    /*-----------*/
//    std::cout << "Undirected graph test begin" << std::endl;
//    graph_test ugtest;
//    ugtest.run();
    /*-----------*/

    std::cout << "Finish!" << std::endl;
}